﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Data.Schema.ScriptDom.Sql;

namespace SQLite2CELib
{
    internal class GroupByConverter
    {
        private string _linqQueryGroupClause = "group new {{ {0} }} by new {{ {1} }} into grouping";

        private Utils _utils;

        public GroupByConverter(Utils utils)
        {
            _utils = utils;
        }

        public StringBuilder Convert(GroupByClause groupByClause, ref List<String> tablesInQuery, out Dictionary<String, List<PropertyDetails>> groupingFieldsSpecs)
        {
            StringBuilder linqGroupByClause;
            ExpressionGroupingSpecification groupingExpression;
            IList<GroupingSpecification> groupingSpecs;
            StringBuilder expressionInsideNewBrackets;
            StringBuilder tableNameInsideLinq;
            List<PropertyDetails> propertyDetailsForGroupingClause;
            KeyValuePair<String, PropertyDetails> matchingClassNameAndTypeInfo;

            //We have list of property details since we might have a group by clause that has same table but multiple columns.
            groupingFieldsSpecs = new Dictionary<String, List<PropertyDetails>>();
            expressionInsideNewBrackets = new StringBuilder();

            groupingSpecs = groupByClause.GroupingSpecifications;
            /* 
             * Consider SQL Query like - 
             * SELECT City, ShipCity FROM Customers 
             * JOIN Orders ON Customers.CustomerId = Orders.CustomerId
             * GROUP BY City, Orders.ShipCity
             * Then LINQ Equivalent for GROUP BY Clause is following
             * group eachCustomer by new { eachOrder.ShipCity, eachCustomer.City   } into grouping
             * which is expected output of this method.
             */
            //Build the expression required inside new {} first.
            foreach (GroupingSpecification eachGroupingSpec in groupingSpecs)
            {
                if (eachGroupingSpec is ExpressionGroupingSpecification)
                {
                    groupingExpression = eachGroupingSpec as ExpressionGroupingSpecification;

                    expressionInsideNewBrackets.Append(_utils.ExtractValueFromExpression(groupingExpression.Expression, out matchingClassNameAndTypeInfo).ToString());

                    if (groupingFieldsSpecs.ContainsKey(matchingClassNameAndTypeInfo.Key) == true)
                    {
                        groupingFieldsSpecs[matchingClassNameAndTypeInfo.Key].Add(matchingClassNameAndTypeInfo.Value);
                    }
                    else
                    {
                        propertyDetailsForGroupingClause = new List<PropertyDetails>();
                        propertyDetailsForGroupingClause.Add(matchingClassNameAndTypeInfo.Value);
                        groupingFieldsSpecs.Add(matchingClassNameAndTypeInfo.Key, propertyDetailsForGroupingClause);
                    }
                    expressionInsideNewBrackets.Append(",");
                }
            }
            //Remove extra comma that appears at last position.
            expressionInsideNewBrackets.Remove(expressionInsideNewBrackets.Length - 1, 1);

            //Build table name.
            tableNameInsideLinq = new StringBuilder();
            foreach (String tableNames in tablesInQuery)
            {
                tableNameInsideLinq.Append("each" + tableNames);
                tableNameInsideLinq.Append(",");
            }
            tableNameInsideLinq.Remove(tableNameInsideLinq.Length - 1, 1);

            linqGroupByClause = new StringBuilder();
            linqGroupByClause.AppendFormat(_linqQueryGroupClause, tableNameInsideLinq, expressionInsideNewBrackets);
            linqGroupByClause.Append(Environment.NewLine);
            return linqGroupByClause;
        }
    }
}
